package com.googlecode.cswish.struts.conversion;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.Map;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;

import org.apache.log4j.Logger;
import org.apache.struts2.util.StrutsTypeConverter;
import org.springframework.stereotype.Service;

import com.googlecode.cswish.Config;
import com.googlecode.cswish.struts.spring.CacheService;
import com.googlecode.cswish.struts.spring.CacheService.Wrapper;
import com.googlecode.cswish.util.FrameConstant;
import com.googlecode.cswish.struts.spring.ObjectFactory;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.XWorkException;
import com.opensymphony.xwork2.conversion.impl.XWorkBasicConverter;
import com.opensymphony.xwork2.inject.Inject;

/**
 * A helper class, it uses the date format configured in <s:date> to
 *  convert the date type
 * <p>
 * You can add the following configuration to "xwork-conversion.properties":
 * java.util.Date=com.googlecode.cswish.struts.conversion.DefaultDateConverter
 * 
 * @author Jerry.Feng 2008-12-1
 */
@Service
public class DefaultDateConverter extends StrutsTypeConverter {

	private static final Logger logger = Logger.getLogger(DefaultDateConverter.class);
	
	@Inject
	private XWorkBasicConverter xworkBasicConverter;
	
	@Resource
	private Config config;
	
	@Resource
	private CacheService cacheService;
	
	private Wrapper<String> dateFormat;
	
	public String getDateFormat() {
		return this.dateFormat.get();
	}

	public void setDateFormat(String dateFormat) {
		this.dateFormat.set(dateFormat);
	}
	
	@PostConstruct
	private void init() {
		dateFormat = cacheService.newVar(String.class, config.getDataFormat());
	}

	@Override
	public Object convertFromString(Map context, String[] values, Class toClass) {
		Date date = null;  
		String sDate = null;  
		if (values != null && values.length > 0) {
			sDate = values[0];  
			if (sDate != null && sDate.length() > 0) {
				// Read date format
				if (dateFormat != null) {
					try {
						// Convert it
						// FIXME: parse the date by the page element setting
						String propertyName = (String) context.get(FrameConstant.CONVERTER_PROPERTY);
						boolean isTime = propertyName != null && (propertyName.equals("createdOn") || propertyName.endsWith("Time") || propertyName.endsWith("time"));
						if (isTime){
							date = new SimpleDateFormat("yyyy-MM-dd HH:mm").parse(sDate);
						} else {
							date = new SimpleDateFormat(config.getDetailDataFormat()).parse(sDate);
						}
					} catch (ParseException e) {
						boolean parsed = false;
						if (!"yyyy-MM-dd".equals(dateFormat)) {
							try {
								date = new SimpleDateFormat("yyyy-MM-dd").parse(sDate);
								parsed = true;
							} catch (ParseException ex) {
							}
						}
						
						if (!parsed) {
							try {
								// Convert it
								date = new SimpleDateFormat("EEE MMM dd yyyy HH:mm:ss Z", Locale.US).parse(sDate);
							} catch (ParseException ex) {
				                throw new XWorkException("Could not parse date", e);
				            }
						}
		            }
				} else {
					if (xworkBasicConverter == null) {
						ObjectFactory.getInstance().getContainer().inject(this);
					}
					date = (Date) xworkBasicConverter.convertValue(context, values, toClass);
				}
			}
		}
		return date;
	}

	@Override
	public String convertToString(Map context, Object o) {
		return convertToString(context, o, false);
	}
	
	public String convertToString(Map context, Object o, boolean isDetailDate) {
		Date date = (Date) o;
		String sDate = null;
		if (o != null) {
			// Read date format 
			String propertyName = (String) context.get(FrameConstant.CONVERTER_PROPERTY);
			boolean isTime = propertyName != null && (propertyName.endsWith("Time") || propertyName.endsWith("time"));
			
			String format;
			if (isTime){
				format = "yyyy-MM-dd HH:mm";
			} else {
				if (isDetailDate) {
					format = config.getDetailDataFormat();
				} else {
					format = dateFormat.get();
				}
			}
			// Convert it
			if (format != null) {
				try {
					sDate = new SimpleDateFormat(format).format(date);
				} catch (Exception ex) {
					logger.error("Wrong data format:" + format, ex);
				}
			}
			if (sDate == null) {
				sDate = (String) xworkBasicConverter.convertValue(context, o, String.class);
			}
		}
		return sDate;
	}
}